using System.Web;
using System.Xml;
using System;

using log4net;
using umlungu.engineX.emailX;
using WebsiteX.CustomerWS;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington
	Started:	2008-06-14
	Status:		release	
	Version:	2.0.0
	Build:		20080614
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20080614:	Initial release.
	---------------------------------------------------------------------------	*/

namespace umlungu.engineX.blogX.WebsiteX {
	/// <summary>Description of the enumeration</summary>
	public enum WebsiteXPassportType {
		/// <summary>test process identifier</summary>
		test = 0,
		/// <summary>error test process identifier</summary>
		testerror = 1,
		/// <summary>identifier description</summary>
		example = 2,
		/// <summary>login user</summary>
		login = 3,
		/// <summary>user summary</summary>
		summary = 4,
		/// <summary>register user</summary>
		register = 5,
		/// <summary>change password</summary>
		change = 6,
		/// <summary>forgotten password</summary>
		forgot = 7,
		/// <summary>unlock account</summary>
		unlock = 8,
		/// <summary>login with token</summary>
		token = 9,
		/// <summary>logout of account</summary>
		logout = 10,
		/// <summary>get hint info</summary>
		hint = 11,
		/// <summary>check hint</summary>
		checkhint = 12,
        /// <summary>share login</summary>
		share = 13
	}
	/// <summary>
	/// The WebsiteXPassport class implements an x_broker to connect to the WebsiteXPassport service
	/// <para>iPassport related broker classes may derived from this class</para>
	/// </summary>
	public class WebsiteXPassport : WebsiteXTemplate {
		#region Invisible properties
        private WebmailCustomerServices customerWS;
		//private Passport.WebsiteXPassportServices passportWS;
		private x_config config;
		//private static string servicesTemplate = "WebsiteXServicesX";
		private WebsiteXUser user;
		#endregion

		#region Constant names
		private const string logid = "WebsiteXPassport";
		private const string logsep = ",";
		private const string root = "<WebsiteXPassport/>";
		private const string CONFIG_ELEM = "PassportX";
		private const string customerID = "customer";
		private const string passportID = "passport";
		private const string emailTemplate = "WebsiteXPassport.xsl";
		#endregion

		#region Constant error strings
		private const string error_no_error = "Not an error";
		private const string error_passport = "WebsiteXPassport error:: ";
		private const string error_passport_test = " test error";
		private const string error_passport_type = " No such passport type";
		private const string error_passport_example = " Example error";
		private const string error_passport_login = " Login error";
		private const string error_passport_summary = " Summary error";
		private const string error_passport_register = " Registration error";
		private const string error_passport_change = " Change password error";
		private const string error_passport_forgot = " Forgotten password error";
		private const string error_passport_locked = " Locked account error";
		private const string error_passport_unlock = " Unlock account error";
		private const string error_passport_email = " user.Email sending error";
		private const string error_passport_gethint = " Hint retrieval error";
		private const string error_passport_checkhint = " Hint answer match error";
		private const string error_passport_result_null = "null result";
		private const string error_passport_result_error = "bad result";
		private const string error_passport_result_login = "Incorrect username or password";
		private const string error_passport_result_token = "Token invalid";
		private const string error_passport_result_usernot = "User not found";
		private const string error_passport_template = " Template error for: ";
		#endregion

		#region Constants
		private const string QUERY_TOKEN = "token";
		private const string FIELD_HINT_ANSWER = "HintAnswerTest";
		private const string FIELD_HINT_QUESTION = "HintQuestion";

		private const string REDIRECTION_LINK = "DestinationLink";
		private const string SELECT_RESULT_CODE = "/Result/ResultCode";
		private const string SELECT_RESULT_DESC = "/Result/Description";
		private const string SELECT_USER_EMAIL = "//EMail";
		private const string SELECT_USER_PASSWORD = "//Password";
		private const string SELECT_USER_QUESTION = "//QuestionID";
		private const string SELECT_USER_ANSWER = "//Answer";
		private const string SELECT_IPASSPORT = "iPassport";

		private const string LINK_REGISTER = "passport_register";
		private const string LINK_FORGOT = "passport_forgot";
		private const string LINK_INFO = "passport_register_info";
		private const string LINK_LOGIN = "passport_login";
		private const string LINK_LOCKED = "passport_locked";
		private const string LINK_HINTBAD = "passport_hintnotok";
		private const string LINK_NOTFOUND = "passport_notfound";

		private const int VERIFY_OK = 0;
		private const int VERIFY_LOGIN_BAD = 1;
		private const int VERIFY_LOGIN_LOCKED = 2;
		private const int VERIFY_TOKEN_INVALID = 3;
		private const int VERIFY_USER_NOTFOUND = 4;
		#endregion

		#region Visible properties
		public static readonly ILog Logger = LogManager.GetLogger(typeof(WebsiteXPassport));
		private WebsiteXPassportType serviceType;
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public WebsiteXPassportType ServiceType {
			get { return serviceType; }
			set { serviceType = value; }
		}
		private XmlNode result;
		/// <summary>The result document</summary>
		/// <value>The XmlDocument containing the result of the web service call to iBurstPassport</value>
		public XmlNode Result {
			get { return result; }
			set { result = value; }
		}
		public XmlDocument ResultDoc {
			get { return result.OwnerDocument; }
		}
		public XmlElement Root {
			get { return result.SelectSingleNode("/") as XmlElement; }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor</summary>
		/// <param name="thispage">The web application Page object</param>
		public WebsiteXPassport(displayX thispage) : base(thispage) {
			initialise();
            user = ((WebsiteXDisplayX)thispage).WebsiteUser ;
            
		}
		#endregion

		#region Public methods
		public override void Process(string type) {
			getType(type);
			switch (serviceType) {
				case WebsiteXPassportType.test:		test(false);	break;
				case WebsiteXPassportType.testerror:	test(true);		break;
				case WebsiteXPassportType.example:	example();		break;
				case WebsiteXPassportType.login:		login(UserProfile.Value(REDIRECTION_LINK)); break;
				case WebsiteXPassportType.token:		token();		break;
				case WebsiteXPassportType.summary:	summary();		break;
				case WebsiteXPassportType.register:	register();		break;
				case WebsiteXPassportType.change:	change();		break;
				case WebsiteXPassportType.forgot:	forgot();		break;
				case WebsiteXPassportType.unlock:	unlock();		break;
				case WebsiteXPassportType.logout:	logout();		break;
				case WebsiteXPassportType.hint:		hint();			break;
				case WebsiteXPassportType.checkhint:	checkhint();	break;
				case WebsiteXPassportType.share:		share();		break;
			}
		}
		#endregion

		#region Protected methods
		#endregion

		#region Private methods
		/// <summary>Gets the type of service</summary>
		private void getType(string thistype) {
			switch (thistype) {
			case "test":		serviceType = WebsiteXPassportType.test;			break;
			case "errortest":	serviceType = WebsiteXPassportType.testerror;	break;
			case "example":		serviceType = WebsiteXPassportType.example;		break;
			case "login":		serviceType = WebsiteXPassportType.login;		break;
			case "token":		serviceType = WebsiteXPassportType.token;		break;
			case "summary":		serviceType = WebsiteXPassportType.summary;		break;
			case "register":	serviceType = WebsiteXPassportType.register;		break;
			case "change":		serviceType = WebsiteXPassportType.change;		break;
			case "forgot":		serviceType = WebsiteXPassportType.forgot;		break;
			case "unlock":		serviceType = WebsiteXPassportType.unlock;		break;
			case "logout":		serviceType = WebsiteXPassportType.logout;		break;
			case "hint":		serviceType = WebsiteXPassportType.hint;			break;
			case "checkhint":	serviceType = WebsiteXPassportType.checkhint;	break;
			case "share":		serviceType = WebsiteXPassportType.share;		break;
			default: throw (new x_exception("error_passport_type", String.Concat(error_passport, error_passport_type)));
			}
		}
		/// <summary>Tests</summary>
		private void test(bool error) {
			Logger.Info(String.Concat(logid, "_test:", (error)? "error" : "no error"));
			result = customerWS.Login("alanben", "BlueBean", true);
			Logger.Info(String.Concat(logid, "_test:", Root.OuterXml));
			//if (error)
			//	throw (new x_exception("error_passport_test", String.Concat(error_passport, error_passport_test)));
		}
		/// <summary>Example</summary>
		private void example() {
			try {
				Logger.Info(String.Concat(logid, "_example:"));
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_example", String.Concat(error_passport_example, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_example", String.Concat(error_passport_example, e.Message, " - ", e.StackTrace)));
			}
		}
		/// <summary>Login to Passport</summary>
		private void login(string link) {
			login();
			summary();
			if (link != "" && link != null) {
				UserProfile.Clear(REDIRECTION_LINK);
				throw (new displayException(link));
			}
		}
		/// <summary>Login to Passport</summary>
		private void login() {
			try {
				Logger.Debug(String.Concat(logid, "_login:", user.Username, "/", user.Password));
				Logger.Debug(String.Concat(logid, "_login:", UIPage.UserProfile.ProfileXml.OuterXml));
				bool expireToken = getRemember();
				Logger.Debug(String.Concat(logid, "_login:", user.Username, "/", user.Password, "/", expireToken));
				result = customerWS.Login(user.Username, user.Password, expireToken);
				Logger.Debug(String.Concat(logid, "_login:", result.OuterXml));

				int verifyResult = verify();
				Logger.Info(String.Concat(logid, "_login:", user.Username, "/", user.Password, "::result:", verifyResult.ToString()));
				if (verifyResult == VERIFY_OK) {	// ie is iPassport User
                    user.Set(Root.GetAttribute("Token"), Root.GetAttribute("UserID"));
                    result = customerWS.GetUserID(Convert.ToInt32(user.UserID));
					user.SetName(Root.SelectSingleNode("FirstName").InnerText, Root.SelectSingleNode("Surname").InnerText);
					Logger.Info(String.Concat(logid, "_login:", user.Token, "/", user.UserID));
					// now set a cookie if it doesnt expire
					if (!expireToken)
						((WebsiteXDisplayX)UIPage).Cooker.Token = user.Token;
				} else if (verifyResult == VERIFY_LOGIN_LOCKED) {	// Too many login attempts
					locked();
				} else if (verifyResult == VERIFY_USER_NOTFOUND) {	// User not found
					throw (new displayException(LINK_NOTFOUND));
				} else if (verifyResult == VERIFY_LOGIN_BAD) {
					if (customerWS.VerifyUsername(user.Username)) {	// ie username exists therefore must be bad password
						// send recruit email...
						throw (new displayException(LINK_FORGOT));
					} else {
						throw (new displayException(LINK_INFO));
					}
				} else {
					Logger.Info(String.Concat(logid, "_login:", verifyResult.ToString()));
				}
			}
			catch (displayException e) {
				throw e;
			}
			catch (WebsiteXPassportException e) {
				throw (new x_exception("error_passport_login", String.Concat(error_passport_login, "::", e.Code, ": '", e.Message, "'")));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_login", String.Concat(error_passport_login, "::", e.Message, " - ", e.StackTrace)));
			}
		}
		/// <summary>Login to Passport (using cookie token)</summary>
		private void token() {
			Logger.Debug(String.Concat(logid, "_token:", user.Cookie));
			token(user.Cookie);
		}
		/// <summary>Login to Passport (using token)</summary>
		private void token(string token) {
			try {
				if (String.IsNullOrEmpty(token))	// try Query parameter
					token = UIPage.GetQueryParam(QUERY_TOKEN);
				Logger.Debug(String.Concat(logid, "_token:", token));

				result = customerWS.Validate(token);
				Logger.Debug(String.Concat(logid, "_token:", Root.OuterXml));
				int verifyResult = verify();
				if (verifyResult == VERIFY_OK) {	// ie is iPassport User
					user.Set(Root.GetAttribute("Token"), Root.GetAttribute("UserID"), Root.SelectSingleNode("UserName").InnerText, Root.SelectSingleNode("Password").InnerText, Root.SelectSingleNode("FirstName").InnerText, Root.SelectSingleNode("Surname").InnerText);
					Logger.Info(String.Concat(logid, "_login:", user.Token, "/", user.UserID));
				} else if (verifyResult == VERIFY_TOKEN_INVALID) {	// token invalid
					Logger.Info(String.Concat(logid, "_token:", verifyResult.ToString()));
					((WebsiteXDisplayX)UIPage).Cooker.Token = "";
					user.Reset();
				} else {
					Logger.Info(String.Concat(logid, "_token:", verifyResult.ToString()));
					throw new Exception(String.Concat("Bad token validate result: ", verifyResult.ToString()));
				}
			}
			catch (displayException e) {
				throw e;
			}
			catch (WebsiteXPassportException e) {
				throw (new x_exception("error_passport_login", String.Concat(error_passport_login, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_login", String.Concat(error_passport_login, e.Message, " - ", e.StackTrace)));
			}
		}
		/// <summary>Register user (ie account holder)</summary>
		private void register() {
			try {
				Logger.Info(String.Concat(logid, "_register:"));
				Logger.Debug(String.Concat(logid, "_register:", UIPage.UserProfile.ProfileXml.OuterXml));
				result = customerWS.RegisterUser(user.Username, user.Password, user.Firstname, user.Surname, user.Email, "", user.Cellno);
				Logger.Debug(String.Concat(logid, "_register:", Root.OuterXml));
				int verifyResult = verify();
				if (verifyResult == VERIFY_OK) {	// ie is iPassport User
				}
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_register", String.Concat(error_passport_register, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_register", String.Concat(error_passport_register, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Change password</summary>
		private void change() {
			try {
				Logger.Info(String.Concat(logid, "_change:"));
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_change", String.Concat(error_passport_change, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_change", String.Concat(error_passport_change, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Get password and hint info</summary>
		private void hint() {
			try {
				Logger.Info(String.Concat(logid, "_hint:", user.Username));
				getHint();
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_gethint", String.Concat(error_passport_gethint, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Check given hint against hint info</summary>
		private void checkhint() {
			try {
				Logger.Info(String.Concat(logid, "_checkhint:", user.Username));
				getHint();
				string answer = user.Get(FIELD_HINT_ANSWER).ToLower();
				if (answer != user.HintAnswer.ToLower()) {
					throw (new displayException(LINK_HINTBAD));
				}
			}
			catch (displayException e) {
				throw e;
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_checkhint", String.Concat(error_passport_checkhint, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_checkhint", String.Concat(error_passport_checkhint, e.Message, " - ", e.StackTrace)));
			}
		}

		private void getHint() {
			result = customerWS.GetHint(user.Username);
			int verifyResult = verify();
			Logger.Debug(String.Concat(logid, "_getHint:", Root.OuterXml));
			if (verifyResult == VERIFY_OK) {
				user.Password = Root.SelectSingleNode(SELECT_USER_PASSWORD).InnerText;
				user.HintID = Root.SelectSingleNode(SELECT_USER_QUESTION).InnerText;
				UserProfile.Add(FIELD_HINT_QUESTION, String.Concat("Hint", user.HintID));
				user.HintAnswer = Root.SelectSingleNode(SELECT_USER_ANSWER).InnerText;
				Logger.Debug(String.Concat(logid, "_getHint:", logsep, "id:", user.HintID, logsep, "answer:", user.HintAnswer, logsep, "password:", user.Password));
			} else {
				throw new x_exception("error_passport_gethint", error_passport_gethint);
			}

		}

		/// <summary>Forgot password</summary>
		private void forgot() {
			try {
				Logger.Info(String.Concat(logid, "_forgot:"));
				result = customerWS.GetPassword(user.Username);
				int verifyResult = verify();
				Logger.Debug(String.Concat(logid, "_forgot:", Root.OuterXml));
				if (verifyResult == VERIFY_OK) {
					XmlNode email = Root.SelectSingleNode(SELECT_USER_EMAIL);
					Logger.Info(String.Concat(logid, "_forgot:", logsep, (email == null) ? "" : email.InnerText));
					send(email.InnerText);
				}
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_forgot", String.Concat(error_passport_forgot, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_forgot", String.Concat(error_passport_forgot, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Get user summary info</summary>
		private void summary() {
			try {
				Logger.Info(String.Concat(logid, "_summary:"));
				// add the summary xml to the content element (ie not page)
				x_page summpage = new x_page(UIPage, "ipassport", "", "", "summary");
				//cactusProducts(summpage);
				UIPage.Content.AppendChild(UIPage.Document.ImportNode(summpage.Content.SelectSingleNode(SELECT_IPASSPORT), true));
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_summary", String.Concat(error_passport_summary, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_summary", String.Concat(error_passport_summary, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Get user summary info</summary>
		private void locked() {
			try {
				result = customerWS.GetUser(user.Username);
				if (verify() == VERIFY_OK) {
					user.Token = Root.GetAttribute("Token");
					Root.SetAttribute("type", "account_locked");
					Root.SetAttribute("url", String.Concat(config.Host, config.Path));
					XmlNode email = Root.SelectSingleNode(SELECT_USER_EMAIL);
					Logger.Info(String.Concat(logid, "_locked:", logsep, (email == null) ? "" : email.InnerText));
					send(email.InnerText);
					user.Reset();
					throw (new displayException(LINK_LOCKED));
				} else {
					throw (new Exception("GetUser not verified"));
				}
			}
			catch (displayException e) {
				throw e;
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_locked", String.Concat(error_passport_locked, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_locked", String.Concat(error_passport_locked, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Unlock account</summary>
		private void unlock() {
			try {
				Logger.Info(String.Concat(logid, "_unlock:", UIPage.Parameters.Token));
				if (!customerWS.UnlockUser(UIPage.Parameters.Token))
					throw (new displayException(LINK_LOCKED));
			}
			catch (displayException e) {
				throw e;
			}
			catch (x_exception e) {
				throw (new x_exception("error_passport_unlock", String.Concat(error_passport_unlock, e.Message)));
			}
			catch (Exception e) {
				throw (new x_exception("error_passport_unlock", String.Concat(error_passport_unlock, e.Message, " - ", e.StackTrace)));
			}
		}

		/// <summary>Logout of account</summary>
		private void logout() {
			string token = UIPage.GetQueryParam(QUERY_TOKEN);
			token = (token == "")? user.Token : token;
			Logger.Info(String.Concat(logid, "_logout:", token));
			customerWS.Logout(token);
			user.Reset();
		}

		/// <summary>Retrieve user token (after registration)</summary>
		private void share() {
			token();
			if (!String.IsNullOrEmpty(user.FriendID) && !String.IsNullOrEmpty(user.FileID))
				throw (new displayException("profile_share_thankyou_"));	// ie need to accept share invite
		}

		#endregion

		#region Private utility methods
		/// <summary>Does the initialisation for the class - common to overloaded constructors</summary>
		private void initialise() {
			config = new x_config();
			log4net.Config.XmlConfigurator.Configure();
			customerWS = new WebmailCustomerServices();
			customerWS.Url = getUrl(customerID);
			Logger.Debug(String.Concat(logid, "_initialise:", customerWS.Url));
		}

		private string getUrl(string configID) {
			return config.Value(String.Concat(CONFIG_ELEM, "/Url[@id='", configID, "']"));
		}

		/// <summary>verifies the result from the web service call</summary>
		private int verify() {
			int returnCode = VERIFY_OK;
			XmlElement resltcode = Root.SelectSingleNode(SELECT_RESULT_CODE) as XmlElement;
			if (resltcode != null) {
				if (resltcode.InnerText != "0") {
					string excode = "error_passport_result_error";
					string exdesc = String.Concat(error_passport, error_passport_result_error, " code: '", resltcode.InnerText, "' description: '", Root.SelectSingleNode(SELECT_RESULT_DESC).InnerText, "'");
					bool throwException = true;
					switch(resltcode.InnerText) {
						case "1000":
							excode = "error_passport_result_login";
							exdesc = error_passport_result_login;
							returnCode = VERIFY_LOGIN_BAD;
							throwException = false;
							break;
						case "1003":
							excode = "error_passport_result_login";
							exdesc = error_passport_result_login;
							returnCode = VERIFY_LOGIN_LOCKED;
							throwException = false;
							break;
						case "2070":
							excode = "error_passport_result_token";
							exdesc = error_passport_result_token;
							returnCode = VERIFY_TOKEN_INVALID;
							throwException = false;
							break;
						case "2053":
							excode = "error_passport_result_usernot";
							exdesc = error_passport_result_usernot;
							returnCode = VERIFY_USER_NOTFOUND;
							throwException = false;
							break;
						default:
							break;
					}
					if (throwException) 
						throw (new WebsiteXPassportException(excode, exdesc));
				}
			} else {
				throw (new WebsiteXPassportException("error_passport_result_null", String.Concat(error_passport, error_passport_result_null)));
			}
			return returnCode;
		}

		/// <summary>Emails the user details, including password</summary>
		private string send(string email) {
			string sendtext = "";
			x_email thisemail = new x_email();
			try {
				thisemail.Bcc = "alanben@umlungu.com";
				thisemail.To = email;
				thisemail.Type = x_emailtype.html;
				thisemail.Send(Root, _GetTemplate(emailTemplate));
				sendtext = thisemail.Message;
			}
			catch(System.Exception e) {
				string msg = String.Concat(error_passport, error_passport_email, e.Message);
				Logger.Info(String.Concat(logid, "_email:", user.Username, logsep, "Email_NotOK", logsep, email, logsep, thisemail.Server, logsep, msg));
				throw(new WebsiteXPassportException("error_passport_email", msg));
			}
			return sendtext;
		}

		private bool getRemember() {
			Logger.Debug(String.Concat(logid, "_getRemember:", "RememberMe:", user.RememberMe));
			bool noremember = (user.RememberMe != "on");
			Logger.Info(String.Concat(logid, "_getRemember:", "noremember:", noremember.ToString()));
			user.RememberMe = "off";	// set off automatically, for safety
			return noremember;
		}

		#endregion
	}
	/// <summary>
	/// An exception class specific to iBurstPassport and derived classes
	/// </summary>
	public class WebsiteXPassportException : x_exception {
		/// <summary>Constructor</summary>
		/// <param name="cde">Error code</param>
		/// <param name="message">Error message</param>
		public WebsiteXPassportException(string cde, string message) : base(cde, message) {
		}
	}
}
